package com.example.protocol.util;
/**
 * 判断接收到的topic是否匹配某个模式topic的一个工具类
 * 使用mqttTopicMatch这个方法就行，mqttTopicMatchOld是一个思路写的很差劲的方法，弃用了
 */
public class TopicMatch {
    /**
     * 判断接收到的topic是否匹配某个模式topic
     *
     * @param srcTopic 接收到的topic
     * @param tgtTopic 保存在程序里的模式topic
     * @return bool true表示匹配，false表示不匹配
     */
    public static boolean mqttTopicMatch(String srcTopic, String tgtTopic) {
        if (tgtTopic.contains("+") == false && tgtTopic.contains("#") == false) {
            // 如果目标topic不含#或者+，那就直接用equals匹配
            return srcTopic.equals(tgtTopic);
        } else {
            String[] srcTopicSp = srcTopic.split("/");
            String[] tgtTopicSp = tgtTopic.split("/");
            // 先判断tgt最后有没有井号，根据mqtt规定，topic的井号只能在最后
            // 如果有
            if (tgtTopicSp[tgtTopicSp.length - 1].equals("#")) {
                // 那么把src的分段在井号及其后面的去掉，因为井号代表不计数量的通配，比较这些没有意义，最后把tgt的井号也去掉
                // 相当于进行下面这几种操作
                // [a/b/c/d] [a/b/#] -> [a/b] [a/b]
                // [a/b/c] [a/b/#] -> [a/b] [a/b]
                // [a/b] [a/b/#] -> [a/b] [a/b]

                // 井号位置必然是tgt分段里面的最后一段
                int numberSignPos = tgtTopicSp.length - 1;

                // 如果src的分段长度小于tgt，那么没有裁剪src的必要
                // 如果src的分段长度大于等于tgt，那么裁剪src在numberSignPos以及之后的部分
                if (srcTopicSp.length >= tgtTopicSp.length) {
                    // 以下内容相当于是python的分片 srcTopicSp = srcTopicSp[0 : numberSignPos]
                    String[] temp = new String[numberSignPos];
                    for (int i = 0; i < numberSignPos; i++) {
                        temp[i] = srcTopicSp[i];
                    }
                    srcTopicSp = temp;
                }

                // 把tgtTopicSp的最后的井号去掉 tgtTopicSp = tgtTopicSp[0 : numberSignPos]
                String[] temp = new String[numberSignPos];
                for (int i = 0; i < numberSignPos; i++) {
                    temp[i] = tgtTopicSp[i];
                }
                tgtTopicSp = temp;
            }
            // 如果没有井号，那么判断分段数量是否相等
            else {
                // 如果两个分段数组的长度不相等，最后也没有井号，那肯定是不匹配的
                if (srcTopicSp.length != tgtTopicSp.length) {
                    return false;
                }
                // 如果两个分段数组的长度相等，最后没有井号，那就需要遍历

            }
            // 到了这一步，两个分段数组的长度必然是相等的，遍历比较，跳过加号
            int ptr = 0;
            while (ptr < tgtTopicSp.length) {
                // 两个分段匹配，指针后移
                if (srcTopicSp[ptr].equals(tgtTopicSp[ptr])) {
                    ptr++;
                }
                // 两分段不匹配，有可能是字符串与加号，或者确实不匹配
                else {
                    // 如果是加号，跳过
                    if (tgtTopicSp[ptr].equals("+")) {
                        ptr++;
                        continue;
                    }
                    // 否则，确实不匹配
                    else {
                        return false;
                    }
                }
            }
            //while走完，说明均匹配
            return true;
        }
    }

    /**
     * 判断接收到的topic是否匹配某个模式topic
     * （这个方法经过简单测试似乎也没问题，但是思路写的很差劲，弃用了）
     * @param srcTopic 接收到的topic
     * @param tgtTopic 保存在程序里的模式topic
     * @return bool true表示匹配，false表示不匹配
     */
    public static boolean mqttTopicMatchOld(String srcTopic, String tgtTopic) {
        if (tgtTopic.contains("+") == false && tgtTopic.contains("#") == false) {
            // 如果目标topic不含#或者+，那就直接用equals匹配
            return srcTopic.equals(tgtTopic);
        } else {
            String[] srcTopicSp = srcTopic.split("/");
            String[] tgtTopicSp = tgtTopic.split("/");
            // 如果两个分段数组的长度不相等，而且tgt的最后也不是井号，那肯定是不匹配的
            if (srcTopicSp.length != tgtTopicSp.length && !tgtTopicSp[tgtTopicSp.length - 1].equals("#")) {
                return false;
            }
            int srcTopicSpPtr = 0;
            int tgtTopicSpPtr = 0;
            while (tgtTopicSpPtr < tgtTopicSp.length) {
                // 两个分段匹配，两指针均后移
                if (srcTopicSp[srcTopicSpPtr].equals(tgtTopicSp[tgtTopicSpPtr])) {
                    tgtTopicSpPtr++;
                    // 但是src的指针需要做判断防止溢出，小于length的情况下才能后移
                    if (srcTopicSpPtr + 1 < srcTopicSp.length) {
                        // 说明src未走完，后移
                        srcTopicSpPtr++;
                        continue;
                    } else {
                        // 说明src走完了，那么需要判断tgt的下一个是不是井号
                        if (tgtTopicSpPtr + 1 < tgtTopicSp.length) {
                            // 说明tgt未走完，后移
                            tgtTopicSpPtr++;
                            if (tgtTopicSp[tgtTopicSpPtr].equals("#")) {
                                // 如果tgt的下一个就是井号，那就说明井号前面的都能匹配，那么整体就是匹配的
                                return true;
                            } else {
                                // tgt下一个不是井号，但src走完了，说明不匹配
                                return false;
                            }
                        } else {
                            // 说明tgt也走完了，src也走完了，也没有进到不匹配的分支去，那只能是匹配
                            return true;
                        }
                    }
                }
                // 两分段不匹配，有可能是字符串与加号，字符串与井号或者确实不匹配
                else {
                    if (tgtTopicSp[tgtTopicSpPtr].equals("+")) {
                        // 分段不匹配，字符串与加号
                        // 先都向后移动一次，判断下一个的分段是否相等
                        tgtTopicSpPtr++;
                        // 注意溢出
                        if (srcTopicSpPtr + 1 < srcTopicSp.length) {
                            // 说明src未走完，后移
                            srcTopicSpPtr++;
                            continue;
                            // 所以这个加号的含义有点像跳过
                        }
                        // 说明src走完了，那么需要判断tgt的下一个是不是井号
                        if (tgtTopicSpPtr + 1 < tgtTopicSp.length) {
                            // 说明tgt未走完，后移
                            tgtTopicSpPtr++;
                            if (tgtTopicSp[tgtTopicSpPtr].equals("#")) {
                                // 如果tgt的下一个就是井号，那就说明井号前面的都能匹配，那么整体就是匹配的
                                return true;
                            } else {
                                // tgt下一个不是井号，但src走完了，说明不匹配
                                return false;
                            }
                        } else {
                            // 说明tgt也走完了，src也走完了，但是有不匹配
                            return false;
                        }
                    } else if (tgtTopicSp[tgtTopicSpPtr].equals("#")) {
                        // tgt是井号，那么继续比较就没啥意义了，匹配
                        return true;
                    } else {
                        // 分段不匹配，且目标分段不等于加号或者井号
                        return false;
                    }
                } // end if
            } // end while
            // 循环结束，说明一直匹配
            return true;
        }// end if
    }
}
